#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<ctype.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<sys/stat.h>
#include<fcntl.h>
 
 
#define SIZE 512
#define ZERO '\0'
#define SEP " "
#define NUM 32
#define SkipPath(p) do { p += (strlen(p)-1); while(*p != '/' )p--; }while(0)
#define SkipSpace(cmd,pos) do{\
    while(1){\
        if(isspace(cmd[pos]))pos++;\
        else break;\
    }}while(0)

#define None_Redir 0
#define In_Redir 1
#define Out_Redir 2
#define App_Redir 3

int redir_type = None_Redir;
char * filename = NULL;
 
 
char cwd[SIZE*2];
char* gArgv[NUM];
int lastcode = 0;
 
void Die()
{
    exit(1);
}
 
const char* GetHome()
{
    const char* Home = getenv("HOME");
    if(Home == NULL) return "/";
    return Home;
}
 
const char* GetUserName()
{
    const char* UserName = getenv("USER");
    if(UserName == NULL) return "NoneName";
    return UserName;
}
 
const char* GetHonstName()
{
    const char* HonstName = getenv("HOSTNAME");
    if(HonstName == NULL) return "NoneHost";
    return HonstName;
}
 
//路径
const char* GetCwd()
{
    const char* Cwd = getenv("PWD");
    //TODO
 
    if(Cwd == NULL) return "NoneCwd";
    return Cwd;
}
 
//CommandLine : output
void MakeCommandLine()
{
    char line[SIZE];
    const char* UserName = GetUserName();
    const char* HonstName = GetHonstName();
    const char* Cwd = GetCwd();
    SkipPath(Cwd);
 
    snprintf(line,sizeof(line),"[%s@%s %s]~ ",UserName,HonstName,strlen(Cwd)==1?"/":Cwd+1);
 
    printf("%s",line);
    fflush(stdout);
}
 
int GetUserCommand(char UserCommand[],size_t n)
{
    char* s = fgets(UserCommand,n,stdin);
    if(s==NULL)return -1;
    UserCommand[strlen(UserCommand)-1] = ZERO;
    return strlen(UserCommand);
}
 
void SplitComman(char command[],size_t n)
{
    gArgv[0] = strtok(command,SEP);
    int index = 1;
    while((gArgv[index++] = strtok(NULL,SEP)));//这样最后不能分割时让argv最后一个元素是NULL
    return;
}
 
void ExecuteCommand()
{
    pid_t id = fork();
    if(id<0)Die();
    else if(id==0)
    {
        //重定向设置
        if(filename!=NULL)
        {
            if(redir_type==In_Redir)
            {
                int fd = open(filename,O_RDONLY);
                dup2(fd,0);
            }
            else if(redir_type==Out_Redir)
            {
                int fd = open(filename,O_WRONLY|O_CREAT,0666);
                dup2(fd,1);
            }
            else if(redir_type==App_Redir)
            {
                int fd = open(filename,O_WRONLY|O_CREAT|O_APPEND,0666);
                dup2(fd,1);
            }
            else
            {

            }
        }

        //child
        execvp(gArgv[0],gArgv);
        exit(errno);
    }
    else
    {
        //father
        int status = 0;
        pid_t rid = waitpid(id, &status, 0);
        if(rid > 0)
        {
            lastcode = WEXITSTATUS(status);
            if(lastcode!=0)printf("%s:%s:%d\n",gArgv[0],strerror(lastcode),lastcode);
        }
    }
}
 
void Cd()
{
    const char* path = gArgv[1];
    if(path==NULL) path=GetHome();
    chdir(path);
 
    //更新环境变量
    char tmp[SIZE*2];
    getcwd(tmp,sizeof(tmp));
    snprintf(cwd,sizeof(cwd),"PWD=%s",tmp);
    putenv(cwd);
}
 
int CheckBuilding()
{
    int yes = 0;
    const char* enter_cmd = gArgv[0];
    if(strcmp(enter_cmd,"cd")==0)
    {
        yes = 1;
        Cd();
    }
    else if(strcmp(enter_cmd,"echo") == 0 && strcmp(gArgv[1],"$?") == 0)
    {
        yes = 1;
        printf("%d\n",lastcode);
        lastcode = 0;
    }
 
    return yes;
}


void CheckRedir(char cmd[])
{
    int pos = 0;
    int end = strlen(cmd);
    while(pos<end)
    {
        if(cmd[pos]=='>')
        {  
            if(cmd[pos+1]=='>')
            {
                cmd[pos++] = 0;
                pos++;
                redir_type = App_Redir;
                SkipSpace(cmd,pos);
                filename = cmd+pos;
            }
            else
            {
                cmd[pos++] = 0;
                redir_type = Out_Redir;
                SkipSpace(cmd,pos);
                filename = cmd+pos;
            }
        }
        else if(cmd[pos]=='<')
        {
            cmd[pos++] = 0;
            redir_type = In_Redir;
            SkipSpace(cmd,pos);
            filename = cmd+pos;
        }
        else 
        {
            pos++;
        }
    }
}
 
int main()
{
    int quit = 0;
    while(!quit)
    {
        //1,自己输出命令行
        MakeCommandLine();
 
        //2.获取用户命令字符串
        char UserCommand[SIZE];
        int n = GetUserCommand(UserCommand,sizeof(UserCommand));
        if(n<=0)return 1;

        //2.1 check redir
        CheckRedir(UserCommand);

 
        //3.命令行字符串分割
        SplitComman(UserCommand,sizeof(UserCommand));
 
        //4.检查是否为内建命令
        n = CheckBuilding(); 
        if(n) continue;
 
        //n.执行命令
        ExecuteCommand();
    }
 
 
    return 0;
}